package org.test4j.mock;

import org.test4j.mock.faking.meta.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.test4j.mock.faking.util.ObjectIdentify.identities;

/**
 * 定义mock行为
 *
 * @param <T> 被mock类
 * @see #MockUp(Object...)
 */
public class MockUp<T> extends AbstractFake {
    public MockUp(Class declaredClass, Object... objects) {
        super(declaredClass, identities(objects));
        this.initFakeMethods();
    }

    protected MockUp() {
        this(new Object[0]);
    }

    protected MockUp(Object... objects) {
        super(identities(objects));
        this.initFakeMethods();
    }

    protected MockUp(String fullClassName, Object... objects) {
        super(fullClassName, identities(objects));
        this.initFakeMethods();
    }

    protected MockUp(Class declaredClass, Set<Integer> fakedHashCodes) {
        super(declaredClass, fakedHashCodes);
        this.initFakeMethods();
    }

    /**
     * 修改字节码实现
     */
    protected void initFakeMethods() {
        Class fakeClass = this.getClass();
        FakeMethods fakeMethods = new FakeMethods(fakedSeqNo);
        Set<String> matched = new HashSet<>();
        while (MockUp.class.isAssignableFrom(fakeClass) && fakeClass != MockUp.class) {
            List<MethodId> methods = new ClassMeta(declaredToFake, fakeClass).methods;
            for (MethodId methodId : methods) {
                if (matched.contains(methodId.methodDesc)) {
                    continue;
                }
                FakeMethod fakeMethod = new FakeMethod(this, methodId);
                fakeMethods.add(fakeMethod);
                matched.add(methodId.methodDesc);
            }
            fakeClass = fakeClass.getSuperclass();
        }
        FakeStates.register(fakeMethods);
    }
}